package com.ovidiojf.timeTable;

import java.util.ArrayList;

public class Table {
	
	public Table(int days, int cols, int rows, ArrayList<Professor> professors) {
		this.cols = cols;
		this.rows = rows;
		this.days = days;
		this.professors = professors;
		
		gens = new int[days][cols][rows];
		
		checkLstProf = new boolean[professors.size()];
		profsCount	 = new int    [professors.size()];
		
	}
	
	public int getFitness() {
		return fitness;
	}
	
	public int getHardConst() {
		return hardConst;
	}

//	public int getSoftConst() {
//		return softConst;
//	}

	public int getHardWeight() {
		return hardWeight;
	}
	
	public int getPedgConst() {
		return pedgConst;
	}

	public int getProfConst() {
		return profConst;
	}

	public void setHardWeight(int hardWeight) {
		this.hardWeight = hardWeight;
	}
	
	public String getInfo() {
		return String.format(" hard = %2d - pedg = %2d - prof = %2d", hardConst, pedgConst, profConst);		
	}

	public void newGame() {
		//randonGame();
		throwProfessorsInClasses();
		shuffleProfessorsInClasses();
	}
	
	/*
	private void randonGame() {
		for(int i=0; i<cols; i++)
			for(int j=0; j<rows; j++)
				for(int k=0; k<days; k++)
					gens[k][i][j] = randonValue();		
	}
	*/
	
	public void throwProfessorsInClasses() {
		int k=0;
		int j=0;
		
		for(int i=0; i<cols; i++) {
			k=0;
			j=0;
			
			for(int p=0; p<professors.size(); p++)
				if (professors.get(p).isClassToTeach(i))
					for (int c=0; c<professors.get(p).classesInClassRom(i); c++) {
						gens[k][i][j] = p;
						
						j++;
						if (j == rows) {
							k++;
							j = 0;
						}
					}
		}
	}
	
	public void shuffleProfessorsInClasses() {
		for(int i=0; i<cols; i++)
			for(int j=0; j<rows; j++)
				for(int k=0; k<days; k++) {
					int l = Tools.random(rows);
					int m = Tools.random(days);
					
					int t         = gens[k][i][j];
					gens[k][i][j] = gens[m][i][l];
					gens[m][i][l] = t;
				}
					
		
	}
	
	public void mutation(float rate) {
	///	randonMutation(rate);
		verticalSwapMutation(rate);
	}
	
	/*
	private void randonMutation(float rate) {
		int count = (int)((rows*cols) * rate / 100);

		count = Tools.random(count);
		
		for(int i=0; i<count; i++){ 
			int p = Tools.random(gens      .length);
			int q = Tools.random(gens[0]   .length);
			int r = Tools.random(gens[0][0].length);
			gens[p][q][r] = randonValue();          
		}
		
	}
	*/
	
	private void verticalSwapMutation(float rate) {
		int count = (int)((cols*8) * rate / 100);
		int t;
		
		count = Tools.random(count);
		
		for(int i=0; i<count; i++){ 
			int c  = Tools.random(cols);
			int d1 = Tools.random(days);
			int d2 = Tools.random(days);
			int r1 = Tools.random(rows);
			int r2 = Tools.random(rows);
			
			t               = gens[d1][c][r1];
			gens[d1][c][r1] = gens[d2][c][r2];
			gens[d2][c][r2] = t;
		}
	}
	
	/*
	private int randonValue() {
		return Tools.random(professors.size());
	}
	*/
	
	public void setFitness(int fitness) {
		this.fitness = fitness;
	}
	
	public int getRows() {
		return rows;
	}

	public void setRows(int rows) {
		this.rows = rows;
	}

	public int getCols() {
		return cols;
	}

	public void setCols(int cols) {
		this.cols = cols;
	}
	
	public int getDays() {
		return days;
	}

	public void setDays(int days) {
		this.days = days;
	}
	
	public void clearProfessor() {
		professors.clear();
	}
	public void addProfessor(Professor p) {
		professors.add(p);
	}
	
	public Professor getProfessor(int day, int col, int row) {
		return professors.get(gens[day][col][row]);
	}
	
	public ArrayList<Professor> getProfList() {
		return professors;
	}
	

    private boolean contains(int v) {
        return checkLstProf[v];
    }
    
    private void setContains(int v) {
        checkLstProf[v] = true;
    }    	
	
    private void resetSequence() {
        for(int i=0; i<checkLstProf.length; i++)
        	checkLstProf[i] = false;
    }
    
    
    private void resetProfsCount() {
        for(int i=0; i<profsCount.length; i++)
        	profsCount[i] = 0;
    }
	
	private int verifyCols() {
		pedgConst = 0;

		for (int i=0; i<cols; i++) {
			for (int k=0; k<days; k++) {
				resetProfsCount();
			
				for(int j=0; j<rows; j++)
					if (profsCount[gens[k][i][j]] > 1) {
						pedgConst++;
					}
					else {
						profsCount[gens[k][i][j]]++;							
					}
			}
		}
		
		return pedgConst;
	}
	
	
	private int verifyRows() {
		int rowOver = 0;
		
		for (int k=0; k<days; k++)
			for (int i=0; i<rows; i++) {
				resetSequence();
				for (int j=0; j<cols; j++)
					if(contains(gens[k][j][i])) 
						rowOver++;
					else
						setContains(gens[k][j][i]);				
			}
		
		return rowOver;
	}
	
	private int verifyProfPreferences() {
		profConst = 0;
		
		for (int k=0; k<days; k++)
			for (int i=0; i<cols; i++)
				for (int j=0; j<rows; j++) 
					if (professors.get(gens[k][i][j]).isDayRestriction(k))
						profConst++;
				
		return profConst;			
	}
	
	/*
	private int verifyProfsInClasses() {
		int profsInWrongClass = 0;
		
		for (int k=0; k<days; k++)
			for (int i=0; i<cols; i++)
				for (int j=0; j<rows; j++)
					if (!professors.get(gens[k][i][j]).isClassToTeach(i))
						profsInWrongClass++;	
		
		profsOutOfClass = profsInWrongClass;
							
		return profsInWrongClass;
	}
	*/
	
	public void calcFitness() {		
		hardConst = verifyRows();
		profConst = verifyProfPreferences();
		pedgConst = verifyCols();
		
		fitness = (hardConst * hardWeight) + (profConst * profConstWeigth) + (pedgConst * pedgConstWeigth);
	}

	private Table verticalMix(Table partner) {
		Table result = new Table(days, cols, rows, professors);

		boolean b = Tools.random(3) % 2 == 0;
		Table t1 = b ? this    : partner;
		Table t2 = b ? partner : this   ;
		
		int cutCol   = Tools.random(cols);
		int i;
		
		for (int k=0; k<days; k++) {
			
			for (i=0; i<cutCol; i++)
				for (int j=0; j<rows; j++)
					result.gens[k][i][j] = t1.gens[k][i][j];
		
			for(      ;i<cols; i++)
				for (int j=0; j<rows; j++)
					result.gens[k][i][j] = t2.gens[k][i][j];		
		}
		
		return result;
	}
	
	/*
	private Table horizontalMix(Table partner) {
		Table result = new Table(days, cols, rows, professors);

		boolean b = Tools.random(3) % 2 == 0;
		Table t1 = b ? this    : partner;
		Table t2 = b ? partner : this   ;
		
		
		int cutRow   = Tools.random(rows);
		int j;
		
		for (int k=0; k<days; k++) 
			for (int i=0; i<cols; i++) {
				
				//for (j=0; j<cutRow; j++)
					//result.gens[k][i][j] = t1.gens[k][i][j];
					copyLine(t1.gens[k][i], result.gens[k][i]);
					
				//for (   ; j<rows; j++)
					//result.gens[k][i][j] = t2.gens[k][i][j];
					copyLine(t2.gens[k][i], result.gens[k][i]);
			}		
		return result;
	}
	
	private void copyLine(int[] src, int[] dest) {
		System.arraycopy(src, 0, dest, 0, cols);
	}	
	*/

	public Table son(Table partner) {
		return verticalMix(partner);
	}

	public int getGen(int i, int j, int k) {
		return gens[i][j][k];
	}

	
	private int		    hardConst;
//	private int 		softConst;
	private int			pedgConst;
	private int 		profConst;
	private int 		fitness;  
	private int[][][]  	gens; 			// the matrix is [z][x][y]
	private int 		cols; 			// x
	private int 		rows; 			// y
	private int 		days; 			// z
	private int[]		profsCount;	
	private boolean[]	checkLstProf;
	private int 		profConstWeigth =  1;
	private int 		pedgConstWeigth =  profConstWeigth * 10;
	private int 		hardWeight      =  pedgConstWeigth * 10;

	private ArrayList<Professor> professors;

}


